package com.jrx.anytxn.transaction.service.impl;

import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.function.UncheckedConsumer;
import com.jrx.anytxn.common.uitl.BeanUtils;
import com.jrx.anytxn.common.uitl.CollectorsUtils;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.customer.entity.CmCustomerRightInfo;
import com.jrx.anytxn.customer.service.ICustomerRightService;
import com.jrx.anytxn.param.entity.*;
import com.jrx.anytxn.param.service.product.*;
import com.jrx.anytxn.param.service.rights.IPrPromotionInfoService;
import com.jrx.anytxn.transaction.bean.*;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.constant.TransErrMsgConstant;
import com.jrx.anytxn.transaction.entity.*;
import com.jrx.anytxn.transaction.mapper.ext.*;
import com.jrx.anytxn.transaction.service.*;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

/**
 * @program: anytxn-cf-transaction
 * @description: 处理还款分配相关操作类
 * @author: xiaoyu.jing
 * @date: 2019-10-12 11:22
 **/
@Service
public class DealWaivePaymentServiceImpl implements IDealWaivePaymentService {

    private static final Logger logger = LoggerFactory.getLogger(DealWaivePaymentServiceImpl.class);

    @Autowired
    private ITlPaymentAllocationLogService tlPaymentAllocationLogService;
    @Resource
    private ExtAmAccountMainInfoMapper extAmAccountMainInfoMapper;
    @Resource
    private ExtAmInterestAccrualInfoMapper extAmInterestAccrualInfoMapper;
    @Autowired
    private IAccountService accountService;
    @Resource
    private ExtTlPaymentAllocationLogMapper extTlPaymentAllocationLogMapper;
    @Resource
    private ExtTlGlInterfaceInfoMapper extTlGlInterfaceInfoMapper;
    @Resource
    private ExtAmRepaymentPlanInfoMapper extAmRepaymentPlanInfoMapper;
    @Autowired
    private IGlInterfaceService glInterfaceService;
    @Autowired
    private IRepaymentPlanService repaymentPlanService;
    @Autowired
    private IDebtCalculationService debtCalculationService;
    @Resource
    private ExtAmLoanAccountInfoMapper extAmLoanAccountInfoMapper;
    @Resource
    private ExtTlTransactionInfoMapper extTlTransactionInfoMapper;
    @Autowired
    private IPrWaiveInterestTableService waiveInterestTableService;
    @Resource
    private ExtAmWaiveRepaymentPlanInfoMapper extAmWaiveRepaymentPlanInfoMapper;
    @Autowired
    private IProductTableService productTableService;
    @Autowired
    private ICustomerRightService customerRightService;
    @Autowired
    private IInterestTableService interestTableService;
    @Autowired
    private IPrPromotionInfoService prPromotionInfoService;


    /**
     * 贴息账单正常还款
     *
     * @param waivePaymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealWaiveBillPay(TlWaivePaymentLog waivePaymentLog, List<AmLoanMainInfo> loanMainInfos, Date bussinessDate) throws TxnException {
        /* 还款额 */
        BigDecimal paymentAmt = waivePaymentLog.getAmount();
        /* 还款分配入库bean */
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();

        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(waivePaymentLog,
                loanMainInfos.get(0), bussinessDate);

        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);

            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));

            // 2.获取还款需要处理的还款计划
            this.getNeedDealWaivePlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1,
                    TransBizConstant.LOAN_PLAN_STATUS_2));

            // 3.过滤还款计划
            this.filterWaivePlanMap(saveBean, bussinessDate);

            // 4.获取贴息交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));

            // 5.贴息累计账户抛帐，获取生成的贴息交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Collections.singletonList(TransBizConstant.INTEREST_ACCRUAL_TYPE_70), bussinessDate);

            // 6.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 7.将这些交易账户按照分配顺序真正分配，生成09交易账户 并关闭交易账户
            paymentAmt = this.repaymentAllocation(saveBean, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 8. 生成09对应的gl流水
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(),loanMainInfo,loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 9.处理还款计划
            this.dealWaivePlan(saveBean, bussinessDate);

            saveBeanList.add(saveBean);
        }


        // 13.保存所信息
        this.saveAllAccts(saveBeanList);
    }

    /**
     * 贴息账单提前还款
     *
     * @param waivePaymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealWaiveBillPrepayCurr(TlWaivePaymentLog waivePaymentLog, List<AmLoanMainInfo> loanMainInfos, Date bussinessDate) throws TxnException {
        /* 还款额 */
        BigDecimal paymentAmt = waivePaymentLog.getAmount();
        /* 还款分配入库bean */
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();

        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(waivePaymentLog,
                loanMainInfos.get(0), bussinessDate);


        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);

            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));

            // 2.获取还款需要处理的还款计划
            this.getNeedDealWaivePlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1,
                    TransBizConstant.LOAN_PLAN_STATUS_0));

            // 3.获取贴息交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));

            // 4.贴息累计账户抛帐，获取生成的贴息交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Collections.singletonList(TransBizConstant.INTEREST_ACCRUAL_TYPE_70), bussinessDate);

            // 5.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 6.将这些交易账户按照分配顺序真正分配，生成09交易账户 并关闭交易账户
            paymentAmt = this.repaymentAllocation(saveBean, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 7. 生成09对应的gl流水
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(),loanMainInfo,loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 8.处理还款计划
            this.dealPrePayWaivePlan(saveBean, loanMainInfo,loanAccountInfo,bussinessDate);

            //9.重新抛帐
            this.reCastAcct(saveBean,  loanMainInfo,  loanAccountInfo,  bussinessDate);

            saveBeanList.add(saveBean);
        }


        // 13.保存所信息
        this.saveAllAccts(saveBeanList);
    }

    /**
     * 贴息账单正常还款
     *
     * @param waivePaymentLog    还款流水
     * @param loanMainInfos 贷款信息列表
     * @param bussinessDate 业务日期
     * @throws TxnException 自定义异常
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void dealWaiveBillPayOff(TlWaivePaymentLog waivePaymentLog, List<AmLoanMainInfo> loanMainInfos, Date bussinessDate) throws TxnException {
        /* 还款额 */
        BigDecimal paymentAmt = waivePaymentLog.getAmount();
        /* 还款分配入库bean */
        List<PaymentSaveBean> saveBeanList = new ArrayList<>();

        // 1.在还款分配历史表 tl_payment_allocation_log生成08还款交易账户
        TlPaymentAllocationLog totalPaymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(waivePaymentLog,
                loanMainInfos.get(0), bussinessDate);

        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            PaymentSaveBean saveBean = new PaymentSaveBean();
            saveBean.setSavaPaymentLog(totalPaymentLog);

            //获取订单账户信息
            AmLoanAccountInfo loanAccountInfo = Optional.ofNullable(extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId()))
                    .orElseThrow(() -> {
                        logger.error("订单交易账户为空，loanId:{}", loanMainInfo.getLoanId());
                        return new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    });
            logger.info("订单账户信息loanAccountInfo:{}", BeanUtils.toMap(loanAccountInfo));

            // 2.获取还款需要处理的还款计划
            this.getNeedDealWaivePlan(saveBean, loanMainInfo, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0,TransBizConstant.LOAN_PLAN_STATUS_1,
                    TransBizConstant.LOAN_PLAN_STATUS_2));


            // 4.获取贴息交易账户
            this.getAlreadyCastAcct(saveBean, loanMainInfo
                    , Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));

            // 4.贴息累计账户抛帐，获取生成的贴息交易账户,并关闭累计
            this.castAccuAcct(saveBean, loanMainInfo, loanAccountInfo, Collections.singletonList(TransBizConstant.INTEREST_ACCRUAL_TYPE_70), bussinessDate);

            // 5.计算欠款
            DebtBean debtBean = debtCalculationService.calculationDebt(saveBean.getNeedDealAcctList());

            // 6.将这些交易账户按照分配顺序真正分配，生成09交易账户 并关闭交易账户
            paymentAmt = this.repaymentAllocation(saveBean, loanAccountInfo, debtBean, paymentAmt, bussinessDate);

            // 7. 生成09对应的gl流水
            List<TlGlInterfaceInfo> saveGlList = glInterfaceService.buildTlGlInterfaceInfoList(saveBean.getPaymentLogSaveList(),loanMainInfo,loanAccountInfo);
            saveBean.addSaveGlList(saveGlList);

            // 8.处理还款计划
            this.dealWaivePlan(saveBean, bussinessDate);

            // 9.处理剩余还款计划
            this.dealPayOffWaivePlan(saveBean, bussinessDate);

            saveBeanList.add(saveBean);
        }


        // 13.保存所信息
        this.saveAllAccts(saveBeanList);
    }

    /**
     * 获取需要处理的还款计划
     *
     * @param saveBean     保存本金利息罚息dto对象
     * @param loanMainInfo 订单信息
     * @param planStatus   还款计划状态
     */
    private void getNeedDealWaivePlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, List<String> planStatus) throws TxnBizException {
        logger.info("获取需要处理的还款计划开始,planStatus:{}", planStatus);
        List<AmWaiveRepaymentPlanInfo> planInfoList = extAmWaiveRepaymentPlanInfoMapper.selectByLoanIdAndStatus(loanMainInfo.getLoanId()
                , planStatus, loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());
        if (CollectionUtils.isEmpty(planInfoList)) {
            String errMsg = String.format(TransErrMsgConstant.NO_PALN_LIST,loanMainInfo.getLoanId());
            logger.error(errMsg);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        //按期分组
        Map<Integer, AmWaiveRepaymentPlanInfo> planInfoMap = planInfoList.stream().collect(Collectors.toMap(AmWaiveRepaymentPlanInfo::getBillingTenor, Function.identity()));
        saveBean.setWaivePlanInfoMap(planInfoMap);
        logger.info("获取需要处理的还款计划结束,planInfoMap:{}", planInfoMap);
    }


    /**
     * 过滤还款计划
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param bussinessDate 业务日期
     */
    private void filterWaivePlanMap(PaymentSaveBean saveBean, Date bussinessDate) {
        //筛选去除还款日小于或等于当前业务日期的还款计划
        Predicate<AmWaiveRepaymentPlanInfo> filterPlan = plan -> plan.getPaymentDueDate().compareTo(bussinessDate) <= 0;
        Map<Integer, AmWaiveRepaymentPlanInfo> repaymentPlanInfoMap = saveBean.getWaivePlanInfoMap().values().stream()
                .filter(filterPlan)//根据过滤条件筛选
                .collect(Collectors.toMap(AmWaiveRepaymentPlanInfo::getBillingTenor, Function.identity()));//按 k - tenor v-plan 的方式组成map
        saveBean.setWaivePlanInfoMap(repaymentPlanInfoMap);
    }

    /**
     * 获取已抛帐本金交易，利息交易，罚息交易，罚息复利交易，分期费用交易账户
     *
     * @param saveBean     保存本金利息罚息dto对象
     * @param loanMainInfo 贷款信息
     * @param statusList   交易账户状态列表
     */
    private void getAlreadyCastAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, List<String> statusList) throws TxnBizException {
        logger.info("获取本金交易，利息交易，分期费用账户开始,statusList:{}", statusList);
        String loanId = loanMainInfo.getLoanId();
        List<String> typeList = Collections.singletonList(TransBizConstant.ACCOUNT_TYPE_17);
        String customerId = loanMainInfo.getCustomerId();
        String tenantId = loanMainInfo.getTenantId();
        List<AmAccountMainInfo> amAccountMainInfoList = extAmAccountMainInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        if (CollectionUtils.isEmpty(amAccountMainInfoList)) {
            return;
        }

        logger.info("筛选交易账户");
        //只筛选拥有对应需要处理还款计划的交易账户，过滤掉不符合条件的，比如逾期还款的当期本金，费用账户
        Predicate<AmAccountMainInfo> filterAcct = acct -> null != saveBean.getWaivePlanInfoMap().get(acct.getBillingTenor());
        amAccountMainInfoList.stream().filter(filterAcct).forEach(saveBean::addUpdateAcct);
        logger.info("获取本金交易，利息交易，分期费用账户结束,updateAcctList.size:{}", saveBean.getUpdateAcctList().size());
    }

    /**
     * 利息/罚息/罚息复利累计抛帐，获取利息/罚息/罚息复利交易，并关闭利息/罚息/罚息复利累计
     *
     * @param saveBean         保存本金利息罚息dto对象
     * @param loanMainInfo     贷款信息
     * @param loanAccountInfo  贷款账户信息
     * @param accuAcctTypeList 累计账户状态列表
     */
    private void castAccuAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, List<String> accuAcctTypeList, Date businessDate) {
        //需要处理的还款计划
        Map<Integer, AmWaiveRepaymentPlanInfo> planInfoMap = saveBean.getWaivePlanInfoMap();
        String loanId = loanMainInfo.getLoanId();
        String customerId = loanMainInfo.getCustomerId();
        String tenantId = loanMainInfo.getTenantId();
        //获取利息/罚息/罚息复利累计
        List<AmInterestAccrualInfo> accuAcctList = extAmInterestAccrualInfoMapper.selectAllAccuAcct(
                loanId, customerId, tenantId, accuAcctTypeList, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));
        if (CollectionUtils.isEmpty(accuAcctList)) {
            return;
        }

        //筛选出符合的累计账户
        Predicate<AmInterestAccrualInfo> getAccuAcct = accuAcct -> null != planInfoMap.get(accuAcct.getBillingTenor());
        accuAcctList = accuAcctList.stream().filter(getAccuAcct).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(accuAcctList)) {
            return;
        }

        UncheckedConsumer<AmInterestAccrualInfo> castAccu = accuAcct -> {
            //通过订单，累计生成利息/罚息/罚息复利交易
            CastAccountTransactionBean intrCastAccrt = accountService.castWaiveInterestAccount(loanMainInfo, loanAccountInfo,
                    accuAcct, businessDate);
            logger.info("轮询当期利息／罚息累计，生成的交易账户intrAcct:{}", BeanUtils.toMap(intrCastAccrt.getAmAccountMainInfo()));
            //关闭原利息/罚息累计
            AmInterestAccrualInfo updateAccuAcct = new AmInterestAccrualInfo();
            updateAccuAcct.setId(accuAcct.getId());
            updateAccuAcct.setTenantId(accuAcct.getTenantId());
            updateAccuAcct.setCustomerId(accuAcct.getCustomerId());
            updateAccuAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
            //添加代偿标识
            if(TransBizConstant.COMPENSATORY_FLAG_1.equals(loanAccountInfo.getCompensatoryFlag())){
                updateAccuAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);
            }
            //保存生成的交易和需要更新的累计
            saveBean.addSaveAcct(intrCastAccrt.getAmAccountMainInfo());
            saveBean.addUpdateAccuAcct(updateAccuAcct);
            saveBean.addTransactionInfo(intrCastAccrt.getTlTransactionInfo());
            saveBean.addSaveGlList(intrCastAccrt.getTlGlInterfaceInfoList());
        };
        //轮询累计账户生成对应的交易账户
        accuAcctList.forEach(CollectorsUtils.tranToUncheck(castAccu));
    }

    /**
     * 对需要分配的交易账户进行还款分配
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param debtBean      欠款信息
     * @param paymentAmt    还款额
     * @param bussinessDate 业务日期
     * @return 还款额
     * @throws TxnException 自定义异常
     */
    private BigDecimal repaymentAllocation(PaymentSaveBean saveBean, AmLoanAccountInfo loanAccountInfo, DebtBean debtBean,
                                           BigDecimal paymentAmt, Date bussinessDate) throws TxnException {
        //需要分配的交易账户
        List<AmAccountMainInfo> needDealAcctList = saveBean.getNeedDealAcctList();

        //08交易账户
        TlPaymentAllocationLog totalAllocationLog = saveBean.getSavaPaymentLog();
        //总欠款
        BigDecimal debtAmt = debtBean.getWaiveAmount();

        //开始分配
        //原来需要更新交易账户
        List<AmAccountMainInfo> oriUpdateAcctList = saveBean.getUpdateAcctList();
        //原来需要新增交易账户
        List<AmAccountMainInfo> oriSaveAcctList = saveBean.getSaveAcctList();
        //需要更新本，利，罚，费交易账户
        List<AmAccountMainInfo> updateAcctList = new ArrayList<>();
        //需要新增本，利，罚，费交易账户
        List<AmAccountMainInfo> saveAcctList = new ArrayList<>();

        for (AmAccountMainInfo needDealAcct : needDealAcctList) {
            logger.info("开始分配交易账户orgiAcct:{},还款金额:{}", BeanUtils.toMap(needDealAcct), paymentAmt);
            //单笔欠款
            BigDecimal acctRemainAmnt = needDealAcct.getCurrBalance();
            logger.info("单笔欠款acctRemainAmnt:{}", acctRemainAmnt);
            //还款金额已分配完
            if (paymentAmt.compareTo(BigDecimal.ZERO) <= 0 && acctRemainAmnt.compareTo(BigDecimal.ZERO) > 0) {
                logger.info("还款金额小于0，结束分配");
                break;
            }

            //真正子交易还款金额
            BigDecimal realPaymentAmnt;
            //还款金额<=欠款金额,生成一笔还款子交易，金额为还款金额
            if (paymentAmt.compareTo(acctRemainAmnt) <= 0) {
                realPaymentAmnt = paymentAmt;
                logger.info("还款金额<=欠款金额，生成一笔还款子交易，金额为还款金额realPaymentAmnt:{}", realPaymentAmnt);
            } else {
                // 还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额
                realPaymentAmnt = acctRemainAmnt;
                logger.info("还款金额>欠款金额，生成一笔还款子交易，金额为欠款金额realPaymentAmnt:{}", realPaymentAmnt);
            }
            paymentAmt = paymentAmt.subtract(realPaymentAmnt);

            //生成对应的09交易账户  添加代偿标识判断
            TlPaymentAllocationLog paymentLog = tlPaymentAllocationLogService.buildTlPaymentAllocationLog(totalAllocationLog,
                    needDealAcct, realPaymentAmnt);
            saveBean.addPaymentLog(paymentLog);


            //关闭原交易账户
            logger.info("关闭原交易账户");
            needDealAcct.setRepayAmount(needDealAcct.getRepayAmount().add(realPaymentAmnt));
            needDealAcct.setCurrBalance(needDealAcct.getCurrBalance().subtract(realPaymentAmnt));
            needDealAcct.setLastPaymentTime(DateUtils.getBusinessTime(new Date(), bussinessDate));
            if(TransBizConstant.COMPENSATORY_FLAG_1.equals(loanAccountInfo.getCompensatoryFlag())){
                needDealAcct.setCompensatoryFlag(TransBizConstant.COMPENSATORY_FLAG_1);
                if (needDealAcct.getCompensatoryDays() == null) {
                    needDealAcct.setCompensatoryDays(0);
                } else {
                    int cmpsNumber = needDealAcct.getCompensatoryDays() + 1;
                    needDealAcct.setCompensatoryDays(cmpsNumber);
                }
            }

            //如果原账户金额已还完
            if (needDealAcct.getCurrBalance().compareTo(BigDecimal.ZERO) <= 0) {
                if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(totalAllocationLog.getPaymentType())
                        || TransBizConstant.REPAYMENT_TYPE_STAGING.equals(totalAllocationLog.getPaymentType())) {
                    needDealAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_7);
                } else {
                    needDealAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
                }
            }

            //轮询原更新队列，如果存在AccountId相同，则更新，否则应该新增
            Predicate<AmAccountMainInfo> isSameAcctId = acct -> needDealAcct.getAccountId().equals(acct.getAccountId());

            long count = oriUpdateAcctList.stream().filter(isSameAcctId).count();
            if (count > 0) {
                logger.info("需要更新orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                updateAcctList.add(needDealAcct);
                oriUpdateAcctList.removeIf(isSameAcctId);
            } else {
                logger.info("需要新增orgiAcct:{}", BeanUtils.toMap(needDealAcct));
                saveAcctList.add(needDealAcct);
                oriSaveAcctList.removeIf(isSameAcctId);
            }
        }

        //重新设置需要保存的交易账户
        saveAcctList.addAll(oriSaveAcctList);
        saveBean.setSaveAcctList(saveAcctList);
        //重新设置需要更新的交易账户
        updateAcctList = this.copyUpdateAcctList(updateAcctList);
        saveBean.setUpdateAcctList(updateAcctList);

        List<AmAccountMainInfo> allAcctList = new ArrayList<>();
        allAcctList.addAll(saveAcctList);
        allAcctList.addAll(updateAcctList);
        allAcctList.addAll(oriUpdateAcctList);
        saveBean.setAllAcctList(allAcctList);

        return paymentAmt;
    }

    /**
     * 生成需要更新的交易账户
     *
     * @param orgiAcctList 原始账户列表
     * @return 交易账户list
     */
    private List<AmAccountMainInfo> copyUpdateAcctList(List<AmAccountMainInfo> orgiAcctList) {
        UnaryOperator<AmAccountMainInfo> copyAcct = orgiAcct -> {
            AmAccountMainInfo acct = new AmAccountMainInfo();
            acct.setId(orgiAcct.getId());
            acct.setTenantId(orgiAcct.getTenantId());
            acct.setCustomerId(orgiAcct.getCustomerId());
            acct.setBillingTenor(orgiAcct.getBillingTenor());
            acct.setRepayAmount(orgiAcct.getRepayAmount());
            acct.setCreditAdjustAmount(orgiAcct.getCreditAdjustAmount());
            acct.setCurrBalance(orgiAcct.getCurrBalance());
            acct.setAccountStatus(orgiAcct.getAccountStatus());
            acct.setLastPaymentTime(orgiAcct.getLastPaymentTime());
            return acct;
        };
        return orgiAcctList.stream().map(copyAcct).collect(Collectors.toList());
    }



    /**
     * 处理还款计划
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param bussinessDate 业务日期
     */
    private void dealWaivePlan(PaymentSaveBean saveBean, Date bussinessDate) {
        logger.info("更新还款计划开始");
        String paymentType = saveBean.getSavaPaymentLog().getPaymentType();
        List<TlPaymentAllocationLog> savePaymentLogList = saveBean.getPaymentLogSaveList();//09还款交易子账户
        Map<Integer, AmWaiveRepaymentPlanInfo> planInfoMap = saveBean.getWaivePlanInfoMap();//所有处理的还款计划
        //先按期分组
        Map<Integer, List<TlPaymentAllocationLog>> repayAmtListMap = savePaymentLogList.stream().collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor));
        repayAmtListMap.forEach(
                //每期按关联账户类型分组求每期还款本金，还款利息，还款罚息，还款费用分别之和
                (tenor, paymentLogList) -> {
                    logger.info("更新还款计划,第{}期", tenor);
                    Map<String, BigDecimal> repayMap = paymentLogList.stream().collect(
                            Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType, CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))
                    );
                    logger.info("更新还款计划,该期的欠款:{}", repayMap);
                    //更新每期还款计划状态，还款本金，还款利息，还款罚息
                    AmWaiveRepaymentPlanInfo plan = planInfoMap.get(tenor);
                    if (null == plan) {
                        return;
                    }
                    logger.info("更新还款计划,plan:{}", BeanUtils.toMap(plan));

                    AmWaiveRepaymentPlanInfo updatePlan = new AmWaiveRepaymentPlanInfo();
                    updatePlan.setId(plan.getId());
                    updatePlan.setCustomerId(plan.getCustomerId());
                    updatePlan.setTenantId(plan.getTenantId());
                    updatePlan.setBillingTenor(plan.getBillingTenor());

                    BigDecimal intrAmt = Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_17)).orElse(BigDecimal.ZERO);
                    updatePlan.setRepayInterest(plan.getRepayInterest().add(intrAmt));
                    plan.setRepayInterest(plan.getRepayInterest().add(intrAmt));
                    if(TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(paymentType)
                            || TransBizConstant.REPAYMENT_TYPE_STAGING.equals(paymentType)) {
                        plan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_7);//方便计算订单最大延滞天数
                        updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_7);
                    }else{
                        plan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_8);//方便计算订单最大延滞天数
                        updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_8);
                    }
                    updatePlan.setPayoffDate(bussinessDate);
                    updatePlan.setTransactionDate(bussinessDate);
                    planInfoMap.put(tenor, plan);
                    saveBean.setWaivePlanInfoMap(planInfoMap);
                    saveBean.getUpdateWaivePlanList().add(updatePlan);
                }
        );
        logger.info("更新还款计划结束");
    }

    /**
     * 处理提前还款之后的还款计划
     *
     * @param saveBean      保存本金利息罚息dto对象
     * @param loanMainInfo  贷款订单
     * @param bussinessDate 业务日期
     */
    private void dealPrePayWaivePlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, Date bussinessDate) throws TxnException {
        logger.info("处理提前还款后的还款计划");
        //剩余本金
        BigDecimal prinBalAmt = loanAccountInfo.getRemainPrincipal();
        logger.info("订单剩余本金prinBalAmt:{}", prinBalAmt);
        //如果还款本金>=剩余本金
        if (prinBalAmt.compareTo(BigDecimal.ZERO) == 0) {
            //提前结清
            this.dealWaivePlan(saveBean, bussinessDate);
            //处理剩余还款计划
            this.dealPayOffWaivePlan(saveBean, bussinessDate);
        } else {
            //提前部分还款
            this.cancelPlan(saveBean);
            this.reBuildPlan(saveBean, loanMainInfo, loanAccountInfo, prinBalAmt, bussinessDate);
        }
    }


    /**
     * 取消原还款计划
     *
     * @param saveBean 保存本金利息罚息dto对象
     */
    private void cancelPlan(PaymentSaveBean saveBean) {
        //所有需要处理的还款计划
        Map<Integer, AmWaiveRepaymentPlanInfo> planInfoMap = saveBean.getWaivePlanInfoMap();
        //获取更新实体
        UnaryOperator<AmWaiveRepaymentPlanInfo> cancelPlan = plan -> {
            AmWaiveRepaymentPlanInfo updatePlan = new AmWaiveRepaymentPlanInfo();
            updatePlan.setId(plan.getId());
            updatePlan.setCustomerId(plan.getCustomerId());
            updatePlan.setTenantId(plan.getTenantId());
            updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_9);
            return updatePlan;
        };

        //需要更新还款计划
        planInfoMap.values().stream()
                .map(cancelPlan)//取消原还款计划
                .forEach(
                    plan ->{
                        saveBean.getUpdateWaivePlanList().add(plan);
                    }
                );

    }

    /**
     * 重新生成还款计划
     *
     * @param saveBean           保存本金利息罚息dto对象
     * @param loanMainInfo       贷款信息
     * @param loanAccountInfo    贷款账户信息
     * @param afterPayPrinBalAmt 修改后的本金余额
     * @param bussinessDate      业务日期
     * @throws TxnException 自定义异常
     */
    private void reBuildPlan(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo,
                             BigDecimal afterPayPrinBalAmt, Date bussinessDate) throws TxnException {
        Integer tenor = loanMainInfo.getTotalTenor() - loanAccountInfo.getCastTenor() + 1;
        //构建生成还款计划请求Bean，
        // 使用订单下个还款日作为重建还款计划的首期还款日，重建还款计划不需要计算首期还款计划
        PrProductTable prProductTable = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(), loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
        if(TransBizConstant.LOAN_DAY_PAY_FLAG_1.equals(prProductTable.getLoanDayPayFlag()) && bussinessDate.compareTo(loanMainInfo.getPostingDate()) == 0){
            bussinessDate = DateUtils.addDay(bussinessDate,1);
        }

        List<AmRepaymentPlanInfo> repaymentPlanInfoList = extAmRepaymentPlanInfoMapper.selectPlanListByCondSelective(loanMainInfo.getCustomerId()
                ,loanMainInfo.getTenantId(),loanMainInfo.getLoanId());
        LoanPlanParamBean paramBean = repaymentPlanService.buildLoanPlanParamBean(loanMainInfo, bussinessDate,
                afterPayPrinBalAmt, tenor, null, loanAccountInfo.getNextPaymentDate());
        paramBean.setPrProductTable(prProductTable);

        List<AmWaiveRepaymentPlanInfo> amWaiveRepaymentPlanInfoList;
        if (TransBizConstant.WAIVE_TYPE_1.equals(loanMainInfo.getWaiveType())) {
            PrWaiveInterestTable prWaiveInterestTable = waiveInterestTableService.findWaiveInterestByInterestId(loanMainInfo.getWaiveInterestId(), loanMainInfo.getTenantId());
            paramBean.setPrWaiveInterestTable(prWaiveInterestTable);
            amWaiveRepaymentPlanInfoList = repaymentPlanService.buildWaiveRepaymentListByProduct(repaymentPlanInfoList,paramBean);
        }else{
            String customerId = loanMainInfo.getCustomerId();
            String tenantId = loanMainInfo.getTenantId();
            Long custRightId = loanMainInfo.getCustRightId();
            if(TransBizConstant.WAIVE_TYPE_2.equals(loanMainInfo.getWaiveType())){
                CmCustomerRightInfo rightInfo = customerRightService.getRightInfoById(custRightId, customerId, tenantId);
                paramBean.setRightInfo(rightInfo);
                paramBean.setDailyRate(loanMainInfo.getOrigDailyRate());
            }else if(TransBizConstant.WAIVE_TYPE_3.equals(loanMainInfo.getWaiveType())){
                PrPromotionInfo prPromotionInfo = prPromotionInfoService.findByPromotionIdAndTenId(loanMainInfo.getPromotionId(),loanMainInfo.getTenantId());
                paramBean.setPrPromotionInfo(prPromotionInfo);
                paramBean.setLabelCode(loanMainInfo.getLabelCode());
                paramBean.setDailyRate(loanMainInfo.getOrigDailyRate());
                if(TransBizConstant.PROMOTION_RATE_FLAG_2.equals(prPromotionInfo.getRateFlag())){
                    PrInterestTable oriInterest = interestTableService.findInterestByInterestId(loanMainInfo.getOriginalInterestTableId(),loanMainInfo.getTenantId());
                    paramBean.setOriginalDailyRate(interestTableService.getDailyRate(oriInterest, bussinessDate));
                }
            }
            amWaiveRepaymentPlanInfoList = repaymentPlanService.buildWaiveRepaymentListByCustRightOrPromotion(repaymentPlanInfoList,paramBean);
        }

        //重新生成还款计划
        amWaiveRepaymentPlanInfoList.stream().filter(plan -> plan.getInterestAmount().compareTo(BigDecimal.ZERO)>0).forEach(
                plan -> {
                    plan.setBillingTenor(plan.getBillingTenor() + loanAccountInfo.getCastTenor() - 1);//将还款计划设置为正确的期数
                    saveBean.getSaveWaivePlanList().add(plan);
                }
        );
    }

    /**
     * 关闭剩余贴息还款计划
     * @param saveBean
     * @param bussinessDate
     */
    private void dealPayOffWaivePlan(PaymentSaveBean saveBean,Date bussinessDate) {
        Map<Integer, AmWaiveRepaymentPlanInfo> planInfoMap = saveBean.getWaivePlanInfoMap();//所有处理的还款计划
        List<AmWaiveRepaymentPlanInfo> updateList =  planInfoMap.values().stream()
                .map(
                    plan -> {
                        if(!TransBizConstant.LOAN_PLAN_STATUS_0.equals(plan.getPlanStatus())){
                            return null;
                        }
                        AmWaiveRepaymentPlanInfo updatePlan = new AmWaiveRepaymentPlanInfo();
                        updatePlan.setId(plan.getId());
                        updatePlan.setCustomerId(plan.getCustomerId());
                        updatePlan.setTenantId(plan.getTenantId());
                        updatePlan.setBillingTenor(plan.getBillingTenor());
                        updatePlan.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_7);
                        updatePlan.setPayoffDate(bussinessDate);
                        updatePlan.setTransactionDate(bussinessDate);
                        return updatePlan;
                    }
        ).filter(Objects::nonNull).collect(Collectors.toList());
        saveBean.getUpdateWaivePlanList().addAll(updateList);
    }

    /**
     * 根据重新生成的还款计划，对当期本金，利息累计，分期费用重新抛帐
     *
     * @param saveBean        保存本金利息罚息dto对象
     * @param loanMainInfo    贷款信息
     * @param loanAccountInfo 贷款账户信息
     * @param bussinessDate   业务日期
     * @throws TxnException 自定义异常
     */
    private void reCastAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, Date bussinessDate) throws TxnException {
        //判断是否有新生成的还款计划  有 提前部分还款  没有 提前结清
        if (CollectionUtils.isEmpty(saveBean.getSaveWaivePlanList())) {//没有直接返回 不做任何处理
            return;
        }

        logger.info("重新抛帐,loanMainInfo:{},loanAccountInfo:{}", BeanUtils.toMap(loanMainInfo), BeanUtils.toMap(loanAccountInfo));
        if (!TransBizConstant.LOAN_STATUS_1.equals(loanAccountInfo.getAccountStatus())) {
            logger.info("订单已结清");
            return;
        }

        //提前还款场景特殊，此时抛帐需要生成当期的交易账户，所以在这里特殊处理
        loanAccountInfo.setCastTenor(loanAccountInfo.getCastTenor() - 1);

        //本金,分期费用，利息累计抛帐
        List<AmRepaymentPlanInfo> planInfoList = extAmRepaymentPlanInfoMapper.selectPlanListByCondSelective(loanMainInfo.getCustomerId()
                ,loanMainInfo.getTenantId(),loanMainInfo.getLoanId());
        List<AmWaiveRepaymentPlanInfo> waivePlanInfoList = saveBean.getSaveWaivePlanList();
        CastWaiveAccountBean castWaiveAccountBean = accountService.castTxnWaiveAccount(loanMainInfo, loanAccountInfo, waivePlanInfoList,planInfoList, bussinessDate, false);
        saveBean.addSaveAccuAcctList(castWaiveAccountBean.getInterestAccrualInfoList());
    }

    /**
     * 保存所有还款信息
     *
     * @param saveBean 保存本金利息罚息dto对象
     */
    private void saveAllAccts(PaymentSaveBean saveBean) {
        TlPaymentAllocationLog savaPaymentLog = saveBean.getSavaPaymentLog();//需要保存的还款总交易账户
        //需要保存的还款子交易账户
        List<TlPaymentAllocationLog> paymentLogSaveList = saveBean.getPaymentLogSaveList().stream()
                .filter(log -> log.getPostingAmount().compareTo(BigDecimal.ZERO) > 0)
                .collect(Collectors.toList());
        paymentLogSaveList.add(savaPaymentLog);
        extTlPaymentAllocationLogMapper.batchInsertSelective(paymentLogSaveList);

        List<AmInterestAccrualInfo> saveAccuAcctList = saveBean.getSaveAccuAcctList();//需要保存的累计账户
        if (CollectionUtils.isNotEmpty(saveAccuAcctList)) {
            extAmInterestAccrualInfoMapper.batchInsertInterestAccrualInfo(saveAccuAcctList);
        }

        List<AmInterestAccrualInfo> updateAccuAcctList = saveBean.getUpdateAccuAcctList();//需要更新的累计账户
        if (CollectionUtils.isNotEmpty(updateAccuAcctList)) {
            extAmInterestAccrualInfoMapper.batchUpdateInterestAccrualInfo(updateAccuAcctList);
        }

        List<AmAccountMainInfo> saveAcctList = saveBean.getSaveAcctList();//保存的交易账户
        if (CollectionUtils.isNotEmpty(saveAcctList)) {
            extAmAccountMainInfoMapper.batchInsertAccount(saveAcctList);
        }
        List<AmAccountMainInfo> updateAcctList = saveBean.getUpdateAcctList();//需要更新交易账户
        if (CollectionUtils.isNotEmpty(updateAcctList)) {
            extAmAccountMainInfoMapper.batchUpdateAccount(updateAcctList);
        }

        List<TlGlInterfaceInfo> glSaveList = saveBean.getSaveGlList();//需要保存的gl流水
        if (CollectionUtils.isNotEmpty(glSaveList)) {
            extTlGlInterfaceInfoMapper.batchInsertSelective(glSaveList);
        }

        List<AmWaiveRepaymentPlanInfo> saveWaivePlanList = saveBean.getSaveWaivePlanList();//需要保存的还款计划
        if (CollectionUtils.isNotEmpty(saveWaivePlanList)) {
            extAmWaiveRepaymentPlanInfoMapper.insertAmWaiveRepaymentPlanInfoList(saveWaivePlanList);
        }

        List<AmWaiveRepaymentPlanInfo> updateWaivePlanList = saveBean.getUpdateWaivePlanList();//需要更新的还款计划
        if (CollectionUtils.isNotEmpty(updateWaivePlanList)) {
            extAmWaiveRepaymentPlanInfoMapper.batchUpdatePlan(updateWaivePlanList);
        }

        List<TlTransactionInfo> transactionInfos = saveBean.getTransactionInfoList();
        if (CollectionUtils.isNotEmpty(transactionInfos)) {
            extTlTransactionInfoMapper.batchInsert(transactionInfos);
        }

    }

    /**
     * 保存所有还款信息
     * * @param saveBeanList 保存本金利息罚息dto对象 list
     */
    private void saveAllAccts(List<PaymentSaveBean> saveBeanList) {
        PaymentSaveBean saveBean = saveBeanList.stream().reduce(new PaymentSaveBean(), PaymentSaveBean::add);
        this.saveAllAccts(saveBean);
    }

}
